/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.web.frontendproject.changedetect.generate.stepexecute;

import com.inspur.edp.lcm.metadata.api.entity.*;
import com.inspur.edp.lcm.metadata.api.entity.uri.MetadataURI;
import com.inspur.edp.lcm.metadata.api.service.MetadataProjectService;
import com.inspur.edp.lcm.metadata.api.service.MetadataService;
import com.inspur.edp.web.common.entity.TerminalType;
import com.inspur.edp.web.common.io.FileUtility;
import com.inspur.edp.web.common.metadata.MetadataGetterParameter;
import com.inspur.edp.web.common.metadata.MetadataTypeEnum;
import com.inspur.edp.web.common.metadata.MetadataUtility;
import com.inspur.edp.web.common.utility.ListUtility;
import com.inspur.edp.web.common.utility.StringUtility;
import com.inspur.edp.web.formmetadata.metadata.FormMetadataContent;
import com.inspur.edp.web.formmetadata.metadata.FormMetadataContentService;
import com.inspur.edp.web.formmetadata.metadata.formdom.FormDOM;
import com.inspur.edp.web.frontendproject.FrontendProjectUtility;
import com.inspur.edp.web.frontendproject.changedetect.ChangeDetectExecuteType;
import com.inspur.edp.web.frontendproject.changedetect.context.ChangeDetectContext;
import com.inspur.edp.web.frontendproject.changedetect.generate.AbstractGenerateChangeDetectStepExecuteService;
import com.inspur.edp.web.frontendproject.changedetect.step.ChangeDetectStepExecuteResult;
import com.inspur.edp.web.frontendproject.changedetect.step.ChangeDetectStepExecuteService;
import com.inspur.edp.web.frontendproject.entity.ChosenFormList;
import com.inspur.edp.web.frontendproject.metadata.FormMetadataManager;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @Title: MetadataChangeStepExecuteImpl
 * @Description: 生成动作依赖元数据变更   步骤
 * @Author: Noah
 * @Version: V1.0
 * @Create: 2022/6/21 16:50
 */
public class MetadataChangeStepExecuteImpl extends AbstractGenerateChangeDetectStepExecuteService implements ChangeDetectStepExecuteService {
    /**
     * lres的正则匹配
     */
    private static final Pattern lresPattern = Pattern.compile(".frm.[a-zA-Z-_]+.lres", Pattern.COMMENTS | Pattern.CASE_INSENSITIVE);
    private static final Pattern mobileLresPattern = Pattern.compile(".mfrm.[a-zA-Z-_]+.lres", Pattern.COMMENTS | Pattern.CASE_INSENSITIVE);

    /**
     * 步骤执行器参数
     *
     * @param context 变更检测上下文参数
     * @return
     */
    @Override
    public ChangeDetectStepExecuteResult execute(ChangeDetectContext context) {
        ChangeDetectStepExecuteResult executeResult = ChangeDetectStepExecuteResult.getPassResult(ChangeDetectExecuteType.Generate);
        MetadataProjectService metadataProjectService = this.getMetadataProjectService();
        // 首先进行元数据变化的检测 如果发生变化 那么认定需要重新生成
        // 工程目录下所有的元数据变更检测
        boolean webRelyMetadataChanged;
        if (context.getTerminalType() == TerminalType.MOBILE) {
            webRelyMetadataChanged = metadataProjectService.isMetadataChanged(context.getProjectPath(), OperationEnum.WEB_GENERATE, this::checkMobileFilePath);

        } else {
            webRelyMetadataChanged = metadataProjectService.isMetadataChanged(context.getProjectPath(), OperationEnum.WEB_GENERATE, this::checkFilePath);

        }
        if (webRelyMetadataChanged) {
            executeResult.setUnPassReason(this.generateReasonWithPrefix("元数据发生变化"));
            return executeResult;
        }

        // 获取此工程下的所有表单元数据
        ChangeDetectStepExecuteResult formRelyMetadataExecuteResult = checkFormRelyMetadataChanged(context, metadataProjectService, ExecuteType.Check);
        if (!formRelyMetadataExecuteResult.isPass()) {
            return formRelyMetadataExecuteResult;
        }

        return executeResult;

    }

//    private static FileFilter getPcFormFileFilter(List<GspMetadata> vueMetadataList) {
//        FileFilter pcFormFileFilter =  new FileFilter() {
//            private List<GspMetadata> exceptMetadataList = vueMetadataList;
//            @Override
//            public boolean accept(File f) {
//                boolean isVueForm = false;
//                for (int i = 0; i < exceptMetadataList.size(); i++) {
//                    GspMetadata metadata = exceptMetadataList.get(i);
//                    if (f.getName().equals(metadata.getHeader().getFileName())) {
//                        isVueForm = true;
//                        break;
//                    }
//
//                }
//                boolean checkFilePath = this.checkFilePath(f);
//                return checkFilePath && !isVueForm;
//            }
//
//            private boolean checkFilePath(File pathname) {
//                if (pathname.isFile()) {
//                    // 当前针对res、lres 仅进行form的验证 其他元数据的res 不进行验证
//                    String filePath = pathname.getPath();
//                    String fileExtension = FileUtility.getFileExtension(filePath);
//                    if (StringUtility.isNullOrEmpty(fileExtension)) {
//                        return true;
//                    }
//                    // 仅处理res和lres  保留表单对应w
//                    if (fileExtension.equals(".res") || fileExtension.equals(".lres")) {
//                        // 表示当前文件为资源文件类型
//                        // 针对res文件  仅仅保留frm的资源文件 其他的资源文件一律不进行检测
//                        if (filePath.endsWith(".frm.res")) {
//                            return true;
//                        }
//
//                        //lres 匹配
//                        Matcher m = lresPattern.matcher(filePath);
//                        return m.find();
//                    }
//                }
//                return true;
//            }
//        };
//        return pcFormFileFilter;
//    }

    private boolean checkFilePath(File pathname) {
        if (pathname.isFile()) {
            // 当前针对res、lres 仅进行form的验证 其他元数据的res 不进行验证
            String filePath = pathname.getPath();
            String fileExtension = FileUtility.getFileExtension(filePath);
            if (StringUtility.isNullOrEmpty(fileExtension)) {
                return true;
            }
            // 仅处理res和lres  保留表单对应
            if (fileExtension.equals(".res") || fileExtension.equals(".lres")) {
                // 表示当前文件为资源文件类型
                // 针对res文件  仅仅保留frm的资源文件 其他的资源文件一律不进行检测
                if (filePath.endsWith(".frm.res")) {
                    return true;
                }

                //lres 匹配
                Matcher m = lresPattern.matcher(filePath);
                return m.find();
            }
        }
        return true;
    }

    private boolean checkMobileFilePath(File pathname) {
        if (pathname.isFile()) {
            // 当前针对res、lres 仅进行form的验证 其他元数据的res 不进行验证
            String filePath = pathname.getPath();
            String fileExtension = FileUtility.getFileExtension(filePath);
            if (StringUtility.isNullOrEmpty(fileExtension)) {
                return true;
            }
            // 仅处理res和lres  保留表单对应
            if (fileExtension.equals(".res") || fileExtension.equals(".lres")) {
                // 表示当前文件为资源文件类型
                // 针对res文件  仅仅保留frm的资源文件 其他的资源文件一律不进行检测
                if (filePath.endsWith(".mfrm.res")) {
                    return true;
                }

                //lres 匹配
                Matcher m = mobileLresPattern.matcher(filePath);
                return m.find();
            }
        }
        return true;
    }

    /**
     * 检测表单依赖的元数据是否发生变更
     *
     * @param context
     * @param metadataProjectService
     * @return
     */
    private ChangeDetectStepExecuteResult checkFormRelyMetadataChanged(ChangeDetectContext context, MetadataProjectService metadataProjectService, ExecuteType executeType) {
        ChangeDetectStepExecuteResult executeResult = ChangeDetectStepExecuteResult.getPassResult(ChangeDetectExecuteType.Generate);
        if (FormMetadataManager.checkFormMetadataExists(context.getProjectPath(), TerminalType.PC, ChosenFormList.getNewInstance(), false, false)) {
            ChangeDetectStepExecuteResult formMetadataListRelyMetadataChangedFlag = checkFormMetadataListRelyMetadataChanged(context, metadataProjectService, TerminalType.PC, executeType);
            if (!formMetadataListRelyMetadataChangedFlag.isPass()) {
                return formMetadataListRelyMetadataChangedFlag;
            }
        }

        if (FormMetadataManager.checkFormMetadataExists(context.getProjectPath(), TerminalType.MOBILE, ChosenFormList.getNewInstance(), false, false)) {
            ChangeDetectStepExecuteResult formMetadataListRelyMetadataChangedFlag = checkFormMetadataListRelyMetadataChanged(context, metadataProjectService, TerminalType.MOBILE, executeType);
            if (!formMetadataListRelyMetadataChangedFlag.isPass()) {
                return formMetadataListRelyMetadataChangedFlag;
            }
        }

        return executeResult;
    }

    private ChangeDetectStepExecuteResult checkFormMetadataListRelyMetadataChanged(ChangeDetectContext context, MetadataProjectService metadataProjectService, TerminalType terminalType, ExecuteType executeType) {
        ChangeDetectStepExecuteResult executeResult = ChangeDetectStepExecuteResult.getPassResult(ChangeDetectExecuteType.Generate);
        List<GspMetadata> formMetadataInCurentProjectList = FrontendProjectUtility.getFormMetadataList(context.getProjectPath(), terminalType);
        if (!ListUtility.isEmpty(formMetadataInCurentProjectList)) {
            for (GspMetadata t : formMetadataInCurentProjectList) {
                ChangeDetectStepExecuteResult checkSpecialFormRelyMetadataChangedFlag = checkSpecialFormRelyMetadataChanged(context, metadataProjectService, t, executeType);
                if (!checkSpecialFormRelyMetadataChangedFlag.isPass()) {
                    return checkSpecialFormRelyMetadataChangedFlag;
                }

            }
        }
        return executeResult;
    }

    private ChangeDetectStepExecuteResult checkSpecialFormRelyMetadataChanged(ChangeDetectContext context, MetadataProjectService metadataProjectService, GspMetadata t, ExecuteType executeType) {
        ChangeDetectStepExecuteResult executeResult = ChangeDetectStepExecuteResult.getPassResult(ChangeDetectExecuteType.Generate);
        if (t.getContent() == null) {
            // 表示通过列表形式获取到的元数据content为空 那么单独进行元数据的读取
            MetadataGetterParameter metadataGetterParameter = MetadataGetterParameter.getNewInstance(t.getHeader().getId(), t.getRelativePath(), MetadataTypeEnum.from(t.getHeader().getType()));
            metadataGetterParameter.setSourceMetadata(t, MetadataTypeEnum.Frm);
            t.setContent(MetadataUtility.getInstance().getMetadataWithDesign(metadataGetterParameter).getContent());
        }
        FormMetadataContent formMetadataContent = (FormMetadataContent) t.getContent();
        FormDOM formContent = FormMetadataContentService.getInstance().getFormContent(formMetadataContent);

        // 存在引用非当前工程下的组合表单，跳过元数据变更检测，对应表单为:
        if (formContent.getModule().getExternalComponents() != null && !formContent.getModule().getExternalComponents().isEmpty()) {
            List<HashMap<String, Object>> externalComponents = formContent.getModule().getExternalComponents();
            for (HashMap<String, Object> externalComponent : externalComponents) {
                if (externalComponent.get("filePath") != null) {
                    String filePath = (String)externalComponent.get("filePath");
                    if (!StringUtility.isNullOrEmpty(filePath)) {
                        String externalCompProjPath = metadataProjectService.getProjPath(filePath);
                        String currentProjPath = metadataProjectService.getProjPath(context.getProjectPath());
                        if (!currentProjPath.equals(externalCompProjPath)) {
                            executeResult.setUnPassReason("存在引用非当前工程下的组合表单，跳过元数据变更检测，对应表单为:" + formContent.getModule().getCode());
                            break;
                        }
                    }
                }
            }
            return executeResult;
        }

        ArrayList<HashMap<String, Object>> currentStateMachineList = formContent.getModule().getStateMachines();
        ChangeDetectStepExecuteResult stateMachineExecuteResult = checkStateMachineMetadataChanged(context, metadataProjectService, formContent, currentStateMachineList, executeType);
        if (!stateMachineExecuteResult.isPass()) {
            return stateMachineExecuteResult;
        }
        ArrayList<HashMap<String, Object>> currentWebCmdList = formContent.getModule().getWebcmds();
        ChangeDetectStepExecuteResult webCmdExecuteResult = checkWebCmdMetadataChanged(context, metadataProjectService, formContent, currentWebCmdList, executeType);
        if (!webCmdExecuteResult.isPass()) {
            return webCmdExecuteResult;
        }
        return executeResult;
    }

    /**
     * 检测命令元数据是否发生变化
     *
     * @param context
     * @param metadataProjectService
     * @param formContent
     * @param currentWebCmdList
     * @return
     */
    private ChangeDetectStepExecuteResult checkWebCmdMetadataChanged(ChangeDetectContext context, MetadataProjectService metadataProjectService, FormDOM formContent, ArrayList<HashMap<String, Object>> currentWebCmdList, ExecuteType executeType) {
        ChangeDetectStepExecuteResult executeResult = ChangeDetectStepExecuteResult.getPassResult(ChangeDetectExecuteType.Generate);
        if (currentWebCmdList != null && !currentWebCmdList.isEmpty()) {
            for (HashMap<String, Object> webCmdItem : currentWebCmdList) {
                String webCmdId = webCmdItem.get("id").toString();
                if (executeType == ExecuteType.Check) {
                    boolean webCmdChangedFlag = metadataProjectService.isRefMetadataChanged(context.getProjectPath(), OperationEnum.WEB_GENERATE, new MetadataURI(webCmdId));
                    if (webCmdChangedFlag) {
                        executeResult.setUnPassReason(this.generateReasonWithPrefix("命令元数据发生变化，对应表单:" + formContent.getModule().getCode() + ",对应命令元数据ID为：" + webCmdId));
                        break;
                    }
                } else {
                    metadataProjectService.updateRefMetadataChanged(context.getProjectPath(), OperationEnum.WEB_GENERATE, new MetadataURI(webCmdId));
                }
            }
        }
        return executeResult;
    }

    /**
     * 检测表单关联状态机
     *
     * @param context
     * @param metadataProjectService
     * @param formContent
     * @param currentStateMachineList
     * @return
     */
    private ChangeDetectStepExecuteResult checkStateMachineMetadataChanged(ChangeDetectContext context, MetadataProjectService metadataProjectService, FormDOM formContent, ArrayList<HashMap<String, Object>> currentStateMachineList, ExecuteType executeType) {
        ChangeDetectStepExecuteResult executeResult = ChangeDetectStepExecuteResult.getPassResult(ChangeDetectExecuteType.Generate);
        if (currentStateMachineList != null && !currentStateMachineList.isEmpty()) {
            for (HashMap<String, Object> smItem : currentStateMachineList) {
                String smItemMetadataId = (String) smItem.get("uri");
                if (!StringUtility.isNullOrEmpty(smItemMetadataId)) {
                    if (executeType == ExecuteType.Check) {
                        boolean smItemMetadataChangedFlag = metadataProjectService.isRefMetadataChanged(context.getProjectPath(), OperationEnum.WEB_GENERATE, new MetadataURI(smItemMetadataId));
                        if (smItemMetadataChangedFlag) {
                            executeResult.setUnPassReason(this.generateReasonWithPrefix("状态机元数据发生变化，对应表单:" + formContent.getModule().getCode() + ",对应状态机元数据ID为：" + smItemMetadataId));
                            break;
                        }
                    } else {
                        metadataProjectService.updateRefMetadataChanged(context.getProjectPath(), OperationEnum.WEB_GENERATE, new MetadataURI(smItemMetadataId));
                    }
                }
            }
        }
        return executeResult;
    }

    /**
     * 针对元数据的变更回写动作
     *
     * @param context
     * @return
     */
    @Override
    public ChangeDetectStepExecuteResult updateChangeset(ChangeDetectContext context) {
        ChangeDetectStepExecuteResult executeResult = ChangeDetectStepExecuteResult.getPassResult(ChangeDetectExecuteType.Generate);
        MetadataProjectService metadataProjectService = this.getMetadataProjectService();
        // 首先进行元数据变化的检测 如果发生变化 那么认定需要重新生成
        if (context.getTerminalType() == TerminalType.MOBILE) {
            metadataProjectService.updateMetadataChanges(context.getProjectPath(), OperationEnum.WEB_GENERATE, this::checkMobileFilePath);

        } else {
            metadataProjectService.updateMetadataChanges(context.getProjectPath(), OperationEnum.WEB_GENERATE, this::checkFilePath);

        }

        // 更新表单依赖元数据变更
        checkFormRelyMetadataChanged(context, metadataProjectService, ExecuteType.UpdateChangeset);

        return executeResult;
    }

    public enum ExecuteType {
        Check,
        UpdateChangeset
    }
}
